import axios, { AxiosInstance, Method, AxiosRequestConfig } from 'axios'
import Qs from 'qs'

import { ElNotification } from 'element-plus'
import { store } from '@/store'
import router from '@/router'

/*
  过滤重复请求！
*/
// 定义接口
interface PendingType {
  url?: string
  method?: Method
  params?: any
  cancel: Function
}
// 取消重复请求
const pendings: Array<PendingType> = []
const CancelToken = axios.CancelToken
// 移除重复请求
function removePending(config: AxiosRequestConfig) {
  const { url, method = 'get', params = '{}' } = config
  const i = pendings.findIndex(v => v.url === url && v.method === method && v.params === params)
  if (i > -1) {
    pendings[i].cancel('操作太频繁，请稍后再试')
    pendings.splice(i, 1)
  }
}

export default class Request {
  private instance: AxiosInstance // axios实例
  // 默认的url从环境变量中获取
  constructor(config: AxiosRequestConfig) {
    this.instance = axios.create(config)
    this.instance.defaults.headers.post['Content-Type'] = 'application/json' // 设置post请求头
    this.requestFn()
    this.responseFn()
  }

  // 请求拦截器
  private requestFn() {
    this.instance.interceptors.request.use(
      (config: any) => {
        // 过滤重复请求
        console.log('-- 请求的config --', config)
        removePending({ url: config.url, method: config.method, params: ['get', 'delete'].includes(config.method) ? JSON.stringify(config.params) : config.data })
        config.cancelToken = new CancelToken(c =>
          pendings.push({ url: config.url, method: config.method, params: ['get', 'delete'].includes(config.method) ? JSON.stringify(config.params) : config.data, cancel: c })
        )
        if (config.method === 'get') {
          config.data = true // 这个是关键点，加入这行就可以了  解决get  请求添加不上content_type
        }
        // 设置token
        const token = store.state.user.token
        token && (config.headers.token = token)
        return config
      },
      (err: any) => {
        ElNotification({ title: '请求错误', message: err, type: 'error' })
        return Promise.reject(err)
      }
    )
  }

  // 响应拦截器
  private responseFn() {
    this.instance.interceptors.response.use(
      (res: any) => {
        removePending({ url: res.config.url, method: res.config.method, params: ['get', 'delete'].includes(res.config.method) ? JSON.stringify(res.config.params) : res.config.data })
        if (res.status === 200) {
          if (res.headers.token) {
            store.commit('user/SET_TOKEN', res.headers.token)
          }
          return Promise.resolve(res.data)
        }
        ElNotification({ title: '接口请求错误', message: res.message, type: 'error' })
      },
      (err: any) => {
        return errHandle(err)
      }
    )
  }

  // 注意：如果需要下载excel等文件流，config需要配置 { responseType: 'blob' }
  get(url: string, params?: any, config?: object) {
    return this.instance.get(url, { params: params, ...config })
  }

  delete(url: string, params?: any, config?: object) {
    return this.instance.delete(url, { params: params, ...config })
  }

  post(url: string, data?: object, config?: any) {
    return this.instance.post(url, config?.headers?.['Content-Type'] === 'application/x-www-form-urlencoded' ? Qs.stringify(data) : data, config)
  }

  put(url: string, data?: object, config?: any) {
    return this.instance.post(url, config?.headers?.['Content-Type'] === 'application/x-www-form-urlencoded' ? Qs.stringify(data) : data, config)
  }

  all<T>(requestArr: Array<T>) {
    return axios.all(requestArr)
  }
}

// 状态码错误的处理函数
function errHandle(err: any) {
  const { response } = err
  // 当服务器返回错误码时
  if (response) {
    switch (response.status) {
      case 400:
        ElNotification({ title: '错误请求', message: response.data.message, type: 'error' })
        break
      case 401:
        ElNotification({ title: '用户信息已变更 / 登录过期', message: response.data.message, type: 'error', duration: 3000 })
        store.commit('user/SET_TOKEN', '')
        store.commit('user/SET_USERINFO', {})
        store.commit('user/SET_ROLES', [])
        store.commit('user/SET_MENULIST', [])
        store.commit('global/REMOVE_NAVIGATIONLIST_ALL')
        store.commit('global/SET_EXPANSION', false)
        store.commit('i18n/SET_LANGUAGE', 'zh')
        sessionStorage.setItem('store', '')
        location.reload()
        setTimeout(() => {
          router.replace('/login')
        }, 200)
        break
      case 403:
        ElNotification({ title: '拒绝访问', message: response.data.message, type: 'error' })
        break
      case 404:
        ElNotification({ title: '请求错误，未找到该资源', message: response.data.message, type: 'error' })
        break
      case 405:
        ElNotification({ title: '请求方法未允许', message: response.data.message, type: 'error' })
        break
      case 408:
        ElNotification({ title: '请求超时', message: response.data.message, type: 'error' })
        break
      case 500:
        ElNotification({ title: '服务器错误', message: response.data.message, type: 'error' })
        break
      case 503:
        ElNotification({ title: '服务器暂时无法使用', message: response.data.message, type: 'error' })
        break
      default:
        ElNotification({ title: '请求数据失败', message: response.data.message, type: 'error' })
    }
    return Promise.reject(response.data)
  } else {
    // 当服务器没有返回结果时，可能是没有网(如果断网了：可以跳转到断网页面)
    if (!window.navigator.onLine) {
      ElNotification({ title: '提示', message: '请检查网络是否连接正常', type: 'error' })
      return
    }
    return Promise.reject(err)
  }
}
